import os
import sys
import stat
import simplejson


from command_parser.option_filter_base import FilterBase
from command_parser.option_base import *
from command_parser.clang_parse.clang_option_dict import OptionCategory
from utils.common_utils import *
from utils.file_type_detect import *

def get_gcc_include_path_search_order(compiler=""):
    if not compiler:
        return
    
    lang = "c"
    if is_cpp_compiler(compiler=compiler):
        lang = "c++"
    command = [compiler, "-E", "-Wp,-v", "-x", lang, "/dev/null"]
    
    p = subprocess.Popen(command, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    _, std_err = p.communicate()
    lines = std_err.decode().splitlines()
    idx = 0
    for idx in range(0, len(lines)):
        if "#include <...> search starts here" in lines[idx]:
            break
    search_paths = lines[idx+1:-1]
    
    search_paths = [path.strip() for path in search_paths]
    
    return search_paths

class ClangFilter(FilterBase):
    
    def __init__(self, context=None, option_context=None):
        self.__context = context
        self.__option_context = option_context
    
    def append_option_include_custom_path(self, cwd="", compiler="", options=[], option=None):
        custom_include_paths = self.__context.CUSTOM_INCLUDE_PATH
        for include_path in custom_include_paths:
            new_option = Option(option="-isystem", parameter=[include_path], type=PREFIX_SPACE)
            options.append(new_option)
    
    
    def append_option_include_path(self, cwd="", compiler="", options=[], option=None):
        
        gcc_search_paths = get_gcc_include_path_search_order(compiler=compiler)
        
        new_options = []
        for path in gcc_search_paths:
            new_option = Option(option="-isystem", parameter=[path], type=PREFIX_SPACE)
            new_options.append(new_option)
        
        options.extend(new_options)
        
    
    def filter_option_default(self, cwd="", compiler="", options=[], option=None):
        
        filter_options = []
        for option in options:
            if not option.keep:
                 filter_options.append(option)
            if option.category:
                if option.category == OptionCategory.NONETIME \
                    or option.category == OptionCategory.DEBUGGING \
                    or option.category == OptionCategory.WARNING \
                    or option.category == OptionCategory.OPTIMIZATION:
                    filter_options.append(option)
        self.remove_options_by_list(options=options, remove_list=filter_options)
    
    def filter_link_options(self, cwd="", compiler="", options=[], option=None):
        
        filter_options = []
        
        for option in options:
            if not option.category:
                continue
            if option.category == OptionCategory.LINKER \
                or option.category == OptionCategory.WARNING:
                    filter_options.append(option)
        
        self.remove_options_by_list(options=options, remove_list=filter_options)
    
    def filter_compile_options(self, cwd="", compiler="", options=[], option=None):
        
        filter_options = []
        
        for option in options:
            if not option.category:
                continue
                
            if option.category == OptionCategory.OPTIMIZATION \
                or option.category == OptionCategory.WARNING:
                    filter_options.append(option)
        
        self.remove_options_by_list(options=options, remove_list=filter_options)
    
    def handle_option_Xclang(self, cwd="", compiler="", options=[], option=None):
        
        if not option.parameter:
            return

        filter_opts = ['-fnative-half-type']
        
        if option.parameter[0] in filter_opts:
            option.keep = False
        
    def handle_option_stdin(self, cwd="", compiler="", options=[], option=None):
        
        src_md5 = str_md5(str(os.getpid()))
        content = ""
        f_mode = os.fstat(sys.stdin.fileno()).st_mode
        if stat.S_ISFIFO(f_mode) or stat.S_ISREG(f_mode) or stat.S_ISCHR(f_mode):
            content = sys.stdin.readlines()
            content = ''.join(content)
            content += str(os.getpid())
            src_md5 = str_md5(content)
        
        if not os.path.exists(self.__context.CACHE_DIR):
            os.mkdir(self.__context.CACHE_DIR)
        
        stdin_cache_file = os.path.join(self.__context.CACHE_DIR, src_md5 + ".cache")
        with open(stdin_cache_file, "w") as f:
            f.write(content)
        
        option.option = ""
        option.parameter[0] = stdin_cache_file
        option.type = PREFIX_MATCH
        option.category = OptionCategory.SOURCE_FILE
        option.keep = True
    
    def convert_dirafter_to_system(self, options=[]):
        
        for idx in range(len(options) - 1, -1, -1):
            if not options[idx].option:
                continue
            
            if not options[idx].option == "-c":
                break
            
            if options[idx].option == "-idirafter":
                options[idx].option = "-isystem"
    
    def append_option_clang_ast(self, options=[]):
        new_option = Option(option="-emit-ast", type=PREFIX_SIMPLE)
        options.append(new_option)
        
    def filter_unkeep_options(self, options=[]):
        
        removal_list = []
        for opt in options:
            if not opt.keep:
                removal_list.append(opt)
        
        self.remove_options_by_list(options=options, remove_list=removal_list)
        
        
    def append_option_compile(self, cwd="", compiler="", options=[], option=None):
        if not options:
            return
        
        new_option = Option(option="-c", type=PREFIX_SIMPLE)
        options.append(new_option)
        
        
    def append_clang_spec_options(self, cwd='', compiler='', options=[], option=None):

        inline_options = [('-fheinous-gnu-extensions', []),
                          ('-fno-inline-functions', []),
                          ('-mllvm', ['-inline-threshold=0']),
                          ('-mllvm', ['-inlinehint-threshold=0'])]
        
        spec_options = []
        
        for i_opt, i_para in inline_options:
            if i_para:
                spec_opt = Option(option=i_opt, parameter=i_para, type=PREFIX_SPACE)
            else:
                spec_opt = Option(option=i_opt, type=PREFIX_SPACE)
            spec_options.append(spec_opt)
        
        options.extend(spec_options)
    
    def append_option_target(self, cwd='', compiler='', options=[], option=None):
        new_option = Option(option="-target", parameter=["aarch64"], type=PREFIX_SPACE)
        options.append(new_option)
        
        
    def append_option_fpic(self, cwd='', compiler='', options=[], option=None):
        
        option = Option(option="-fpic", type=PREFIX_SIMPLE)
        options.append(option)
    
    
    def append_option_flto(self, cwd='', compiler='', options=[], option=None):
        
        option = Option(option="-flto", type=PREFIX_SIMPLE)
        options.append(option)
    
    
    def append_option_debug(self, cwd="", compiler="", options=[], option=None):
        
        option = Option(option="-g", type=PREFIX_SIMPLE)
        options.append(option)
    
    
    def append_option_wno_error(self, cwd="", compiler="", options=[], option=None):
        
        option = Option(option="-Wno-everything", type=PREFIX_SIMPLE)
        options.append(option)